/*
 * Copyright 2007. Mount Sinai Hospital, Toronto, Canada.
 * 
 * Licensed under the GNU Lesser General Public License, Version 2. You
 * can find a copy of the license at:
 * 
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 * 
 * IN NO EVENT SHALL MOUNT SINAI HOSPITAL BE LIABLE TO ANY PARTY FOR DIRECT, 
 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 
 * PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, 
 * EVEN IF MOUNT SINAI HOSPITAL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 
 * DAMAGE.
 * 
 * MOUNT SINAI HOSPITAL SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND 
 * ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". 
 * MOUNT SINAI HOSPITAL HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, 
 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 
 */
package com.sinai.mshab.client.ui.basic;

import java.util.ArrayList;
import java.util.Iterator;

import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sinai.mshab.client.util.Answer;
import com.sinai.mshab.client.ui.FormComponent;
import com.sinai.mshab.client.ui.aggregate.DataReceiver;
import com.sinai.mshab.client.ui.aggregate.FormData;

/**
 * Creates a choice component. This widget takes in a list of choices. If no
 * choices are given, the choice will be a Yes/No radio buttons.
 * 
 * @author serge
 */
public class RadioChoiceComponent extends BasicFormComponent {

	/** The types of questions handled by this widget */
	private boolean yes_no_question = false;

	private boolean radio_choices_question = false;

	/** We need to have access to our widgets */
	private RadioButton yes_radio, no_radio;

	private ArrayList radioList;

	private String choices[];

	private boolean horizontal;

	/**
	 * The first constructor to choice object. No choices are provided to this
	 * constructor thus it defaults to a Yes/No question.
	 * 
	 * @param dbFieldName
	 *            The database field.
	 * @param tableName
	 *            the name of the database table where the radio button
	 *            information will be stored.
	 * @param description
	 *            The text of the question.
	 */
	public RadioChoiceComponent(String dbFieldName, String tableName,
			String description) {
		initializeYesNo(dbFieldName, tableName, description, null, null, true);
	}

	/**
	 * Create a new <code>ChoiceComponent</code> with an empty field name and
	 * just the <code>description</code>.
	 * 
	 * @param description
	 *            The description attached to this field name.
	 */
	public RadioChoiceComponent(String description) {
		initializeYesNo("ignore", "ignore", description, null, null, true);
	}

	/**
	 * <p>
	 * Create a new radio button that does not link to the database. This is
	 * primarily used for objects in conditionals. The horizontal flag will
	 * dictate how the underlying components will be arranged. If horizontal is
	 * true, the arrangement is as follows:
	 * </p>
	 * description | widget | quantifier | error_msg
	 * <p>
	 * If horizontal is false, the arrangement is as follows:
	 * </p>
	 * description <br />
	 * widget <br />
	 * quantifier <br />
	 * error_msg
	 * 
	 * @param description
	 *            The description of the widget.
	 * @param horizontal
	 *            flag to indicate whether horizontal or vertical.
	 */
	public RadioChoiceComponent(String description, boolean horizontal) {
		initializeYesNo("ignore", "ignore", description, quantifier, null,
				horizontal);
	}

	/**
	 * Create a new radio button that does not link to the database. This is
	 * primarily used for objects in conditionals.
	 * 
	 * @param description
	 *            The description of the widget.
	 * @param quantifier
	 *            The quantifier of the widget.
	 */
	public RadioChoiceComponent(String description, String quantifier) {
		initializeYesNo("ignore", "ignore", description, quantifier, null, true);
	}

	/**
	 * <p>
	 * Create a new radio button that does not link to the database. This is
	 * primarily used for objects in conditionals. The horizontal flag will
	 * dictate how the underlying components will be arranged. If horizontal is
	 * true, the arrangement is as follows:
	 * </p>
	 * description | widget | quantifier | error_msg
	 * <p>
	 * If horizontal is false, the arrangement is as follows:
	 * </p>
	 * description <br />
	 * widget <br />
	 * quantifier <br />
	 * error_msg
	 * 
	 * @param description
	 *            The description of the widget.
	 * @param quantifier
	 *            The quantifier of the widget.
	 * @param horizontal
	 *            flag to indicate whether horizontal or vertical.
	 */
	public RadioChoiceComponent(String description, String quantifier,
			boolean horizontal) {
		initializeYesNo("ignore", "ignore", description, quantifier, null,
				horizontal);
	}

	/**
	 * Creates a new ChoiceComponent using multiple choices provided in
	 * <code>choices</code>.
	 * 
	 * @param description
	 *            The text of the question.
	 * @param choices
	 *            Array of multiple answers.
	 */
	public RadioChoiceComponent(String description, String choices[]) {
		initializeMultiChoice("ignore", "ignore", description, null, null,
				choices, true);
	}

	/**
	 * Creates a new ChoiceComponent using multiple choices provided in
	 * <code>choices</code>.
	 * 
	 * @param description
	 *            The text of the question.
	 * @param choices
	 *            Array of multiple answers.
	 * @param horizontal
	 *            whether the radio buttons are vertical or horizontal.
	 */
	public RadioChoiceComponent(String description, String choices[],
			boolean horizontal) {
		initializeMultiChoice("ignore", "ignore", description, null, null,
				choices, horizontal);
	}

	/**
	 * Creates a new ChoiceComponent using multiple choices provided in
	 * <code>choices</code>.
	 * 
	 * @param dbFieldName
	 *            The database field.
	 * @param tableName
	 *            the name of the database table where the textbox information
	 *            will be stored.
	 * @param description
	 *            The text of the question.
	 * @param choices
	 *            Array of multiple answers.
	 * @param horizontal
	 *            whether the radio buttons are vertical or horizontal.
	 */
	public RadioChoiceComponent(String dbFieldName, String tableName,
			String description, String choices[], boolean horizontal) {
		initializeMultiChoice(dbFieldName, tableName, description, null, null,
				choices, horizontal);
	}

	/**
	 * Creates a new "Yes/No" <code>ChoiceComponent</code>.
	 * 
	 * @param dbFieldName
	 *            The database field name to which this
	 *            <code>ChoiceComponent</code> belongs.
	 * @param description
	 *            The description attached with this choice component.
	 */
	private void initializeYesNo(String dbFieldName, String tableName,
			String description, String quantifier, String error_msg,
			boolean horizontal) {

		yes_no_question = true;

		// all our widgets go to the horizontal panel
		String choice_group = description + "" + dbFieldName + ""
				+ (int) (Math.random() * 200000);
		HorizontalPanel buttonPanel = new HorizontalPanel();
		yes_radio = new RadioButton(choice_group, "Yes");
		no_radio = new RadioButton(choice_group, "No");
		buttonPanel.add(yes_radio);
		buttonPanel.add(no_radio);

		// setting alts
		yes_radio.setTitle(description + " yes");
		no_radio.setTitle(description + " no");

		// adding click listeners
		yes_radio.addClickListener(new MyListener());
		no_radio.addClickListener(new MyListener());

		// no should be defaulted
		no_radio.setChecked(true);

		// add to the radio list.
		radioList = new ArrayList();
		radioList.add(yes_radio);
		radioList.add(no_radio);

		// use BasicFormComponent's initialize to keep code clean
		super.initialize(dbFieldName, tableName, description, quantifier,
				error_msg, buttonPanel, horizontal);
	}

	private void initializeMultiChoice(String dbFieldName, String tableName,
			String description, String quantifier, String error_msg,
			String[] choices, boolean horizontal) {

		this.choices = choices;
		this.horizontal = horizontal;
		radio_choices_question = true;
		radioList = new ArrayList();
		ComplexPanel buttonPanel;

		if (horizontal) {
			buttonPanel = new HorizontalPanel();
		} else {
			buttonPanel = new VerticalPanel();
		}

		// adding the choices
		String checkbox_class = choices[0] + dbFieldName + " "
				+ (int) (Math.random() * 2000000);
		for (int i = 0; i < choices.length; i++) {
			RadioButton r = new RadioButton(checkbox_class, choices[i]);

			if (choices[i].equalsIgnoreCase("No")) {
				r.setChecked(true);
			}
			r.addClickListener(new MyListener());
			r.setTitle(description + " " + choices[i]);
			radioList.add(r);
			buttonPanel.add(r);
		}

		// use BasicFormComponent's initialize to keep code clean
		super.initialize(dbFieldName, tableName, description, quantifier,
				error_msg, buttonPanel, horizontal);
	}

	/**
	 * Returns true if we were able to select the radio button with the name
	 * radioName and false otherwise.
	 * 
	 * @param radioName
	 *            the name of the radio button to check.
	 * @return true if we were able to select the radio button.
	 */
	public boolean select(String radioName) {

		boolean result = false;

		// go through all the radio buttons in this RadioChoiceComponent.
		for (int i = 0; i < radioList.size(); i++) {
			RadioButton button = (RadioButton) radioList.get(i);

			// if button's text is the same as the checkbox we're looking for,
			// check it.
			if (button.getText().equals(radioName)) {
				button.setChecked(true);
				result = true;

				// done searching, break.
				break;
			}
		}

		return result;
	}

	public void sendAnswer(DataReceiver receiver) {
		receiver.sendData(getAnswer());
	}

	public Answer getAnswer() {
		Answer answer = new Answer();

		// How we retrieve the answer depends on the type of question
		// Yes/No Question
		if (yes_no_question) {
			String choice = yes_radio.isChecked() ? "Yes" : "No";

			answer.add(tableName, dbfieldName, choice);
		}
		// Multiple Radio Buttons Question
		else if (radio_choices_question) {
			for (int i = 0; i < radioList.size(); i++) {
				if (((RadioButton) radioList.get(i)).isChecked()) {
					answer.add(tableName, dbfieldName, ((RadioButton) radioList
							.get(i)).getText());
					break;
				}
			}
		}

		answer.add(hiddenFields);
		return answer;
	}

	/**
	 * Checks the radio button with the value specified
	 * 
	 * @param answer
	 *            the value whos radio button should be checked.
	 */
	public boolean setAnswer(String answer) {
		boolean successfully_set = false;
		if (yes_no_question) {
			if (answer.equals("Yes")) {
				yes_radio.setChecked(true);
				no_radio.setChecked(false);
				successfully_set = true;
			} else {
				yes_radio.setChecked(false);
				no_radio.setChecked(true);
				successfully_set = true;
			}
		} else {
			for (int i = 0; i < radioList.size(); i++) {
				RadioButton radio_button = (RadioButton) radioList.get(i);
				boolean value = radio_button.getText().equals(answer);
				radio_button.setChecked(value);
				if (value) {
					successfully_set = true;
				}
			}
		}
		return successfully_set;
	}

	/**
	 * Informs observers of change to this widget.
	 */
	private class MyListener implements ClickListener {
		public void onClick(Widget sender) {
			notifyObservers(RadioChoiceComponent.this);
		}
	}

	/**
	 * Sets custom style
	 * 
	 * @param style
	 */
	public void setLabelStyle(String style) {
		description_label.setStyleName(style);
	}

	/**
	 * Sets custom style
	 * 
	 * @param style
	 */
	public void setRadioStyle(String style) {
		for (int i = 0; i < radioList.size(); i++) {
			((RadioButton) radioList.get(i)).setStyleName(style);
		}
	}

	/**
	 * @see FormComponent#addClickListener(ClickListener)
	 */
	public void addClickListener(ClickListener listener) {

		// go through all the radio buttons in radioList and add the listener
		// to them.
		for (int i = 0; i < radioList.size(); i++) {
			RadioButton radio = (RadioButton) radioList.get(i);
			radio.addClickListener(listener);
		}
	}

	public Object clone() {
		RadioChoiceComponent a;
		if (yes_no_question)
			a = new RadioChoiceComponent(dbfieldName, tableName, description);
		else
			a = new RadioChoiceComponent(dbfieldName, tableName, description,
					choices, horizontal);

		if (subquestion)
			a.setAsSubQuestion();

		return a;
	}

	/**
	 * Populate this widget with FormData using an Answer object
	 * 
	 * @param answer
	 *            the answer object containing the data.
	 */
	public boolean populate(Answer answer) {

		// if the db does not contain an answer for us, do nothing.
		if (answer == null) {
			return false;
		}

		// walking through all the fields-values of answer
		Iterator field_names = answer.getHashMap().keySet().iterator();
		while (field_names.hasNext()) {
			String field_name = (String) field_names.next();

			// if field name matches our widget's field name, use this value
			if (field_name.equalsIgnoreCase(dbfieldName)) {
				String value = (String) answer.getHashMap().get(field_name);
				return setAnswer(value);
			}
		}
		return false;
	}

	/**
	 * Populate this widget with FormData using a Table name
	 */
	public boolean populate() {
		Answer a = FormData.getData(tableName);
		return populate(a);
	}

	public HorizontalPanel toReport() {

		// construct an answer and ship it off to toPrint(String)
		String answer = "";

		for (int i = 0; i < radioList.size(); i++) {
			RadioButton radioButton = (RadioButton) radioList.get(i);
			if (radioButton.isChecked()) {
				answer += radioButton.getText() + ", ";
			}
		}
		if (!answer.equals("")) {
			// clean up the String before shipping it off
			answer = answer.trim();
			answer = answer.substring(0, answer.lastIndexOf(','));
		}

		return super.toPrint(answer);
	}

	/**
	 * Always returns false because a <code>RadioChoiceComponent</code> always
	 * has some value.
	 * 
	 * @see FormComponent#isEmpty()
	 */
	public boolean isEmpty() {

		// a RadioChoiceComponent is never false because it always has a default
		// value.
		return false;
	}

	/**
	 * Returns the {@link RadioButton} that has it's text set to
	 * <code>text</code>. If no such RadioButton is found, null is returned.
	 * 
	 * @param text
	 *            the text of the RadioButton we want.
	 * @return the {@link RadioButton} that has it's text set to
	 *         <code>text</code>.
	 */
	public RadioButton getButton(String text) {
		RadioButton result = null;

		// go through all the RadioButtons to find the button we want.
		for (int i = 0; i < radioList.size(); i++) {
			RadioButton button = (RadioButton) radioList.get(i);
			if (button.getText().equals(text)) {
				result = button;
			}
		}

		return result;
	}
}
